home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / ArchiveUtils / JumpBack / Source / Subprocess.m < prev    next >
Encoding:
Text File  |  1995-06-12  |  7.7 KB  |  342 lines

  1.  
  2. //======================================================================
  3. //
  4. //    Portions written by FreemanSoft Inc.
  5. //
  6. //    FreemanSoft disclaims any warranty of any kind, expressed or implied,
  7. //    as to this source code's fitness for any particular use.
  8. //
  9. //    For more information, use the following electronic mail addresses:
  10. //     
  11. //        info@FreemanSoft.com    general questions
  12. //        support@FreemanSoft.com    technical questions
  13. //
  14. //======================================================================
  15.  
  16.  
  17. /*
  18.     Subprocess.m    (v10)
  19.     by Charles L. Oei
  20.     pty support by Joe Freeman
  21.     Subprocess Example, Release 2.0
  22.     NeXT Computer, Inc. 
  23. */
  24.  
  25. #import "Subprocess.h"
  26. #import <sgtty.h>    // needed to compile under Release 1.0
  27. #import <appkit/nextstd.h>
  28. #import <appkit/Application.h>
  29. #import <appkit/Panel.h>
  30.  
  31. #define BUFFERSIZE 2048
  32. #define    PTY_TEMPLATE "/dev/pty??"
  33. #define    PTY_LENGTH 11
  34.  
  35. static void showError();
  36.  
  37.  
  38. /*==========================================================
  39.  *
  40.  * Private Instance Methods
  41.  *
  42.  *==========================================================*/
  43.  
  44. @interface Subprocess(Private)
  45. - childDidExit;
  46. - fdHandler:(int)theFd;
  47. @end
  48.  
  49. @implementation Subprocess(Private)
  50.  
  51. - childDidExit
  52.     // cleanup after a child process exits
  53. {
  54.     if (childPid)
  55.     {
  56.     DPSRemoveFD(fromChild);
  57.     close(fromChild);
  58.     fclose(fpToChild);
  59.     childPid=0;    // specify that child is dead
  60.     if (delegate && [delegate respondsTo:@selector(subprocessDone)])
  61.         [delegate perform:@selector(subprocessDone)];
  62.     }
  63.     return self;
  64. }
  65.  
  66. - fdHandler:(int)theFd
  67.     // DPS handler for output from subprocess
  68. {
  69.     static int count;
  70.     static char buffer[BUFFERSIZE];
  71.  
  72.     if (((count = read(theFd, buffer, BUFFERSIZE-1)) < 0) || (!count))
  73.     {
  74.     [self childDidExit];
  75.     return self;
  76.     }
  77.     buffer[count] = '\0';
  78.     if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
  79.     [delegate perform:@selector(subprocessOutput:) with:(void *)&buffer];
  80.     return self;
  81. }
  82.  
  83. @end
  84.  
  85.  
  86. /*==========================================================
  87.  *
  88.  * Private Utility Routines
  89.  *
  90.  *==========================================================*/
  91.  
  92. static void
  93. showError (const char *errorString, id theDelegate)
  94.     // ensure errors never get dropped on the floor
  95. {
  96.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  97.     [theDelegate
  98.         perform:@selector(subprocessError:)
  99.         with:(void *)errorString];
  100.     else if (NXApp)    // no delegate, but we're running w/in an App
  101.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  102.     else
  103.     perror(errorString);
  104. }
  105.  
  106. static void
  107. fdHandler (int theFd, id self)
  108.     // DPS handler for output from subprocess
  109. {
  110.     [self fdHandler:theFd];
  111. }
  112.  
  113. static void
  114. getptys (int *master, int *slave)
  115.     // attempt to setup the ptys
  116. {
  117.     char device[PTY_LENGTH];
  118.     char *block, *num;
  119.     char *blockLoc; // specifies the location of block for the device string
  120.     char *numLoc; // specifies the pty name with the digit ptyxD
  121.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  122.     
  123.     struct sgttyb setp =
  124.     {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  125.     struct tchars setc =
  126.     {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  127.     struct ltchars sltc =
  128.     {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  129.     int    lset =
  130.     (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  131.     int    setd = NTTYDISC;
  132.     
  133.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  134.     blockLoc = &device[ strlen("/dev/pty") ];
  135.     numLoc = &device[ strlen("/dev/pty?") ];
  136.     msLoc = &device[ strlen("/dev/") ];
  137.     for (block = "pqrs"; *block; block++)
  138.     {
  139.     *blockLoc = *block;
  140.     for (num = "0123456789abcdef"; *num; num++)
  141.     {
  142.         *numLoc = *num;
  143.         *master = open(device, O_RDWR);
  144.         if (*master >= 0)
  145.         {
  146.         *msLoc = 't';
  147.         *slave = open(device, O_RDWR);
  148.         if (*slave >= 0)
  149.         {
  150.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  151.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  152.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  153.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  154.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  155.             return;
  156.         }
  157.         }
  158.     } /* hunting through a bank of ptys */
  159.     } /* hunting through blocks of ptys in all the right places */
  160.     *master = -1;
  161.     *slave = -1;
  162. }
  163.  
  164.  
  165. @implementation Subprocess
  166.  
  167. /*==========================================================
  168.  *
  169.  * Public Factory Methods
  170.  *
  171.  *==========================================================*/
  172.  
  173. + new:(const char *)subprocessString
  174.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  175. {
  176.     return
  177.     [Subprocess
  178.         new:subprocessString
  179.         withDelegate:nil
  180.         andPtySupport:NO
  181.         andStdErr:YES];
  182. }
  183.  
  184. + new:(const char *)subprocessString
  185.     withDelegate:theDelegate
  186.     andPtySupport:(BOOL)wantsPty
  187.     andStdErr:(BOOL)wantsStdErr
  188.     // creates a new instance of Subprocess and corresponding UNIX process
  189. {
  190.     int pipeTo[2];        // for non-Pty support
  191.     int pipeFrom[2];
  192.     static int master;        // file descriptor for master/slave pty
  193.     static int slave;
  194.     int    tty, numFds, fd;    // for temporary use
  195.     int processGroup;
  196.     int pidChild;        // needed because childPid does not exist
  197.                 // until Subprocess is instantiated
  198.  
  199.     if (wantsPty)
  200.     {
  201.         tty = open("/dev/tty", O_RDWR);
  202.     getptys(&master,&slave);
  203.     if (master <= 0 || slave <= 0)
  204.     {
  205.         showError("Error grabbing ptys for subprocess.", theDelegate);
  206.         return self;
  207.     }
  208.     // remove the controlling tty if launched from a shell,
  209.     // but not Workspace;
  210.     // so that we have job control over the parent application in shell
  211.     // and so that subprocesses can be restarted in Workspace
  212.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  213.     {
  214.         ioctl(tty, TIOCNOTTY, 0);
  215.         close(tty);
  216.     }
  217.     }
  218.     else
  219.     {
  220.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  221.     {
  222.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  223.         return self;
  224.     }
  225.     }
  226.     
  227.     switch (pidChild = vfork())
  228.     {
  229.     case -1:    // error
  230.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  231.     return self;
  232.  
  233.     case 0:    // child
  234.     if (wantsPty)
  235.     {
  236.         dup2(slave, 0);
  237.         dup2(slave, 1);
  238.         if (wantsStdErr)
  239.         dup2(slave, 2);
  240.     }
  241.     else
  242.     {
  243.         dup2(pipeTo[0], 0);
  244.         dup2(pipeFrom[1], 1);
  245.         if (wantsStdErr)
  246.         dup2(pipeFrom[1], 2);
  247.     }
  248.     
  249.     numFds = getdtablesize();
  250.     for (fd=3; fd<numFds; fd++)
  251.         close(fd);
  252.  
  253.     processGroup = getpid();
  254.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  255.     setpgrp (0, processGroup);
  256.     
  257.     // we exec a /bin/sh so that cmds are easier to specify for the user
  258.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  259.     perror("vfork (child)"); // should never gets here tho
  260.     exit(1);
  261.  
  262.     default:    // parent
  263.     self = [super new];
  264.     [self setDelegate:theDelegate];
  265.     childPid = pidChild;
  266.  
  267.     if (wantsPty)
  268.     {
  269.         close(slave);
  270.         
  271.         fpToChild = fdopen(master, "w");
  272.         fromChild = master;
  273.     }
  274.     else
  275.     {
  276.         close(pipeTo[0]);
  277.         close(pipeFrom[1]);
  278.     
  279.         fpToChild = fdopen(pipeTo[1], "w");
  280.         fromChild = pipeFrom[0];
  281.     }
  282.  
  283.     setbuf(fpToChild, NULL);
  284.     DPSAddFD(
  285.         fromChild,
  286.         (DPSFDProc)fdHandler,
  287.         (id)self,
  288.         NX_MODALRESPTHRESHOLD+1);
  289.     return self;
  290.     }
  291. }
  292.  
  293. /*==========================================================
  294.  *
  295.  * Public Instance Methods
  296.  *
  297.  *==========================================================*/
  298.  
  299. - send:(const char *)string withNewline:(BOOL)wantNewline
  300. {
  301.     fputs(string, fpToChild);
  302.     if (wantNewline)
  303.         fputc('\n', fpToChild);
  304.     return self;
  305. }
  306.  
  307. - send:(const char *)string
  308. {
  309.     [self send:string withNewline:YES];
  310.     return self;
  311. }
  312.  
  313. - terminateInput
  314.     // effectively sends an EOF to the child process stdin
  315. {
  316.     fclose(fpToChild);
  317.     return self;
  318. }
  319.  
  320. - terminate:sender
  321. {
  322.     if (childPid)
  323.     {
  324.     kill(childPid+1, SIGTERM);
  325.     [self childDidExit];
  326.     }
  327.     return self;
  328. }
  329.  
  330. - setDelegate:anObject
  331. {
  332.     delegate = anObject;
  333.     return self;
  334. }
  335.  
  336. - delegate
  337. {
  338.     return delegate;
  339. }
  340.  
  341. @end
  342.